package com.hp.ecs.xqa.util.orm;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
 * @author fanhoujun 2007-9-17
 */
public class Dao extends HibernateDaoSupport {

	public List find(final String hql, final Object... args) {

		return createQuery(hql, args).list();
	}

	public PageList page(int page, int pageSize, final String hql, final Object... args) {
		final int index = hql.toLowerCase().indexOf(" from ");
		int totalCount = ((Number)find("select count(*) " + (index < 0 ? hql : hql.substring(index))).get(0))
			.intValue();
		PageList pageList = new PageList(page, pageSize);
		pageList.setTotalCount(totalCount);
		pageList.setResult(findLimit(page * pageSize, pageSize, hql, args));
		return pageList;
	}

	public Object findOne(final String hql, final Object... args) {
		return first(findLimit(1, hql, args));
	}

	private Object first(final List list) {
		return list.isEmpty() ? null : list.get(0);
	}

	public List findLimit(int limit, final String hql, final Object... args) {
		return createQuery(hql, args).setMaxResults(limit).list();
	}

	public List findLimit(int begin, int limit, final String hql, final Object... args) {
		return createQuery(hql, args).setFirstResult(begin).setMaxResults(limit).list();
	}

	public <T> T get(final Class<T> clazz, final Serializable id) {
		return getHibernateTemplate().get(clazz, id);
	}

	public <T> T load(final Class<T> clazz, final Serializable id) {
		return getHibernateTemplate().load(clazz, id);
	}

	public void load(Object vo, Serializable id) {
		if (!getHibernateTemplate().contains(vo)) {
			getHibernateTemplate().load(vo, id);
		}
	}

	public <T> List<T> find(final Class<T> clazz) {
		return createCriteria(clazz).list();
	}

	public <T> T findOne(final Class<T> clazz) {
		return (T)first(createCriteria(clazz).setMaxResults(1).list());
	}

	public void delete(final Object entities) {
		if (entities instanceof Collection) {
			getHibernateTemplate().deleteAll((Collection)entities);
		} else {
			getHibernateTemplate().delete(entities);
		}
	}

	public void update(final Object entity) {
		getHibernateTemplate().update(entity);
	}

	public Serializable insert(final Object entity) {
		return save(entity);
	}

	private Serializable save(Object entity) {
		checkId(entity);
		return getHibernateTemplate().save(entity);
	}

	public void insertOrUpdate(final Object entities) {
		if (entities instanceof Collection) {
			for (Iterator it = ((Collection)entities).iterator(); it.hasNext();) {
				saveOrUpdate(it.next());
			}
		} else {
			saveOrUpdate(entities);
		}
	}

	private boolean checkId(Object entity) {
		LongKeyGenerator generator = LongKeyGenerator.getKeyGenerator(getSessionFactory());
		return generator.checkId(entity);
	}

	private void saveOrUpdate(Object entity) {
		if (checkId(entity)) {
			getHibernateTemplate().save(entity);
		} else {
			getHibernateTemplate().saveOrUpdate(entity);
		}
	}

	public int excute(final String hql, final Object... args) {
		return createQuery(hql, args).executeUpdate();
	}

	private Query createQuery(final String hql, final Object[] args) {
		final Query query = getSession().createQuery(hql);
		if (args != null) {
			for (int i = 0; i < args.length; i++) {
				query.setParameter(i, args[i]);
			}
		}
		return query;
	}

	public <T> List<T> sort(final Class<T> clazz, final String orderBy, final boolean isAsc) {
		return createCriteria(clazz, orderBy, isAsc).list();
	}

	private Criteria createCriteria(Class entityClass, String orderBy, boolean isAsc) {
		final Order order = isAsc ? Order.asc(orderBy) : Order.desc(orderBy);
		return createCriteria(entityClass).addOrder(order).setCacheable(true);
	}

	public Criteria createCriteria(final Class entityClass) {
		return getSession().createCriteria(entityClass);
	}

	private Criteria createCriteria(final Class entityClass, final Criterion criterion) {
		return createCriteria(entityClass).add(criterion);
	}

	public <T> List<T> find(final Class<T> clazz, final String propertyName, final Object value) {
		return createCriteria(clazz, Restrictions.eq(propertyName, value)).list();
	}

	public <T> List<T> find(final Class<T> clazz, final String propertyName, final Object value, final String orderBy,
		final boolean isAsc) {
		return createCriteria(clazz, orderBy, isAsc, Restrictions.eq(propertyName, value)).list();
	}

	private Criteria createCriteria(final Class clazz, final String orderBy, final boolean isAsc,
		final Criterion criterions) {
		return createCriteria(clazz, criterions).addOrder(isAsc ? Order.asc(orderBy) : Order.desc(orderBy));
	}

	public <T> T findOne(final Class<T> clazz, final String propertyName, final Object value) {
		final List list = createCriteria(clazz, Restrictions.eq(propertyName, value)).setMaxResults(1).list();
		return (T)first(list);
	}
}
